/*
* Copyright 2011 Licel LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.licel.jcardsim.crypto;
import javacard.framework.Util;
import javacard.security.CryptoException;
import javacard.security.Key;
import javacard.security.Signature;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* Implementation
* <code>Signature</code> with symmetric keys based
* on BouncyCastle CryptoAPI.
* @see Signature
*/
public class SymmetricSignatureImpl extends Signature {
Mac engine;
byte algorithm;
boolean isInitialized;
public SymmetricSignatureImpl(byte algorithm) {
this.algorithm = algorithm;
}
public void init(Key theKey, byte theMode) throws CryptoException {
init(theKey, theMode, null, (short) 0, (short) 0);
}
public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen) throws CryptoException {
if (theKey == null) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
if (!theKey.isInitialized()) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
if (!(theKey instanceof SymmetricKeyImpl)) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
CipherParameters cipherParams = null;
BlockCipher cipher = ((SymmetricKeyImpl) theKey).getCipher();
if (bArray == null) {
cipherParams = ((SymmetricKeyImpl) theKey).getParameters();
} else {
if (bLen != cipher.getBlockSize()) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
cipherParams = new ParametersWithIV(((SymmetricKeyImpl) theKey).getParameters(), bArray, bOff, bLen);
}
switch (algorithm) {
case ALG_DES_MAC4_NOPAD:
engine = new CBCBlockCipherMac(cipher, 32, null);
break;
case ALG_DES_MAC8_NOPAD:
engine = new CBCBlockCipherMac(cipher, 64, null);
break;
case ALG_DES_MAC4_ISO9797_M1:
engine = new CBCBlockCipherMac(cipher, 32, new ZeroBytePadding());
break;
case ALG_DES_MAC8_ISO9797_M1:
engine = new CBCBlockCipherMac(cipher, 64, new ZeroBytePadding());
break;
case ALG_DES_MAC4_ISO9797_M2:
engine = new CBCBlockCipherMac(cipher, 32, new ISO7816d4Padding());
break;
case ALG_DES_MAC8_ISO9797_M2:
engine = new CBCBlockCipherMac(cipher, 64, new ISO7816d4Padding());
break;
case ALG_DES_MAC8_ISO9797_1_M2_ALG3:
engine = new ISO9797Alg3Mac(new DESEngine(), 64, new ISO7816d4Padding());
break;
case ALG_DES_MAC4_PKCS5:
engine = new CBCBlockCipherMac(cipher, 32, new PKCS7Padding());
break;
case ALG_DES_MAC8_PKCS5:
engine = new CBCBlockCipherMac(cipher, 64, new PKCS7Padding());
break;
case ALG_AES_MAC_128_NOPAD:
engine = new CBCBlockCipherMac(cipher, 128, null);
break;
case ALG_AES_CMAC_128:
engine = new CMac(cipher, 128);
break;
case ALG_HMAC_SHA1:
engine = new HMac(new SHA1Digest());
break;
case ALG_HMAC_SHA_256:
engine = new HMac(new SHA256Digest());
break;
case ALG_HMAC_SHA_384:
engine = new HMac(new SHA384Digest());
break;
case ALG_HMAC_SHA_512:
engine = new HMac(new SHA512Digest());
break;
case ALG_HMAC_MD5:
engine = new HMac(new MD5Digest());
break;
case ALG_HMAC_RIPEMD160:
engine = new HMac(new RIPEMD160Digest());
break;
default:
CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
break;
}
engine.init(cipherParams);
isInitialized = true;
}
public short getLength() throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
return (short) engine.getMacSize();
}
public byte getAlgorithm() {
return algorithm;
}
public void update(byte[] inBuff, short inOffset, short inLength) throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
engine.update(inBuff, inOffset, inLength);
}
public short sign(byte[] inBuff, short inOffset, short inLength, byte[] sigBuff, short sigOffset) throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
if ((algorithm == ALG_DES_MAC8_NOPAD || algorithm == ALG_DES_MAC4_NOPAD)
&& ((inLength % 8) != 0)) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
engine.update(inBuff, inOffset, inLength);
short processedBytes = (short) engine.doFinal(sigBuff, sigOffset);
engine.reset();
return processedBytes;
}
public boolean verify(byte[] inBuff, short inOffset, short inLength, byte[] sigBuff, short sigOffset, short sigLength) throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
if ((algorithm == ALG_DES_MAC8_NOPAD || algorithm == ALG_DES_MAC4_NOPAD)
&& ((inLength % 8) != 0)) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
engine.update(inBuff, inOffset, inLength);
byte[] sig = new byte[getLength()];
engine.doFinal(sig, (short) 0);
engine.reset();
return Util.arrayCompare(sig, (short) 0, sigBuff, sigOffset, (short) sig.length) == 0;
}
public void setInitialDigest(byte[] bytes, short s, short s1, byte[] bytes1, short s2, short s3) throws CryptoException {
throw new UnsupportedOperationException("Not supported yet.");
}
public short signPreComputedHash(byte[] bytes, short s, short s1, byte[] bytes1, short s2) throws CryptoException {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean verifyPreComputedHash(byte[] bytes, short s, short s1, byte[] bytes1, short s2, short s3) throws CryptoException {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getPaddingAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getMessageDigestAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getCipherAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
}